/****

    activequant - activestocks.eu

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

	
	contact  : contact@activestocks.eu
    homepage : http://www.activestocks.eu

****/
package org.activequant.data.preparation;

import org.activequant.core.domainmodel.InstrumentSpecification;
import org.activequant.core.domainmodel.data.Candle;
import org.activequant.core.domainmodel.data.CandleSeries;
import org.activequant.core.domainmodel.data.MarketDataEntity;
import org.activequant.core.domainmodel.data.Quote;
import org.activequant.core.domainmodel.data.QuoteSeries;
import org.activequant.core.domainmodel.data.TimeSeries;
import org.activequant.core.domainmodel.data.TradeIndication;
import org.activequant.core.domainmodel.data.TradeIndicationSeries;
import org.activequant.core.types.TimeStamp;
import org.activequant.core.util.TimeSeriesUtils;

/**
 * An abstract base class, implements the IFilter interface.<br/>
 * <br>
 * <b>History:</b><br>
 *  - [25.02.2007] Created (Erik Nijkamp)<br>
 *
 *  @author Erik Nijkamp
 */
public abstract class FilterBase implements IFilter {
	/**
	 * an empty FilterBase(implements IFilter) constructor
	 */
	public FilterBase() {
		
	}
	
	public <U extends MarketDataEntity<U>, T extends TimeSeries<U>>
	T[] process(Class<U> clazz, T... series) throws Exception {
		return processSeries(clazz, series);
	}
	/**
	 * an abstract method. Needs to be implemented
	 * @param <U>
	 * @param <T>
	 * @param clazz
	 * @param series
	 * @return
	 * @throws Exception
	 */
	protected abstract <U extends MarketDataEntity<U>, T extends TimeSeries<U>>
	T[] processSeries(Class<U> clazz, T... series) throws Exception;
	/**
	 * returns a clone of the given series(T) (where T extends TimeSeries)
	 * @param <U>
	 * @param <T>
	 * @param clazz
	 * @param series
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected <U extends MarketDataEntity<U>, T extends TimeSeries<U>>
	T cloneSeries(Class<U> clazz, T series) {
		// clone
		return (T) series.clone();
	}
	/**
	 * returns a clone T[] of the given series(T[]) (where T extends TimeSeries)
	 */
	protected <U extends MarketDataEntity<U>, T extends TimeSeries<U>>
	T[] cloneSeries(Class<U> clazz, T[] series) {
		// clone
		return TimeSeriesUtils.cloneSeries(clazz, series);
	}
	/**
	 * clears the elements of each TimeSeries in the given series(T[]) (where T extends TimeSeries)
	 * @param <U>
	 * @param <T>
	 * @param clazz
	 * @param series
	 */
	protected <U extends MarketDataEntity<U>, T extends TimeSeries<U>>
	void clearSeries(Class<U> clazz, T[] series) {
		// clear
		TimeSeriesUtils.clearSeries(clazz, series);
	}
	/**
	 * Returns a new Candle, Quote or TradeIndication depending on which one of these 3 is the class of the given element(U)
	 * @param <U>
	 * @param element
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected <U extends MarketDataEntity<U>> 
	U newElement(U element) {
		U result = null;
		if(Candle.class.isAssignableFrom(element.getClass())) {
			result = (U) (Object) new Candle();
		} else if(Quote.class.isAssignableFrom(element.getClass())) {
			result = (U) (Object) new Quote();
		} else {
			result = (U) (Object) new TradeIndication();
		}
		return result;
	}
	/**
	 * if the given series(T) is a:<br/>
	 * <strong>1.</strong> CandleSeries -> returns a new Candle<br/>
	 * <strong>2.</strong> QuoteSeries -> returns a new Quote<br/>
	 * <strong>3.</strong> TradeIndicationSeries -> returns a new TradeIndication
	 * @param <U>
	 * @param element
	 * @param spec
	 * @param stamp
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected <U extends MarketDataEntity<U>>
	U newElement(U element, InstrumentSpecification spec, TimeStamp stamp, double... values) {
		U result = null;
		if(Candle.class.isAssignableFrom(element.getClass())) {
			result = (U) (Object) new Candle(spec);
		} else if(Quote.class.isAssignableFrom(element.getClass())) {
			result = (U) (Object) new Quote(spec);
		} else {
			result = (U) (Object) new TradeIndication(spec);
		}
		result.setTimeStamp(stamp);
		setDoubles(result, values);
		return result;
	}
	/**
	 * if the given series(T) is a:<br/>
	 * <strong>1.</strong> CandleSeries -> returns a new Candle<br/>
	 * <strong>2.</strong> QuoteSeries -> returns a new Quote<br/>
	 * <strong>3.</strong> TradeIndicationSeries -> returns a new TradeIndication
	 * @param <U>
	 * @param <T>
	 * @param clazz
	 * @param series
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected <U extends MarketDataEntity<U>, T extends TimeSeries<U>>
	U newElement(Class<U> clazz, T series) {
		if(CandleSeries.class.isAssignableFrom(series.getClass())) {
			Candle candle = new Candle();
			return (U) (Object) candle;
		} else if(QuoteSeries.class.isAssignableFrom(series.getClass())) {
			Quote quote = new Quote();
			return (U) (Object) quote;
		} else if(TradeIndicationSeries.class.isAssignableFrom(series.getClass())) {
			TradeIndication trade = new TradeIndication();
			return (U) (Object) trade;
		}	
		throw new IllegalArgumentException("Unknown series.");
	}
	/**
	 * if the given series(T) is a:<br/>
	 * <strong>1.</strong> CandleSeries -> returns a new Candle using the given spec(InstrumentSpecification), stamp(TimeStamp) and values(double...) to set its openPrice(double), highPrice(double), lowPrice(double) and closePrice(double)<br/>
	 * <strong>2.</strong> QuoteSeries -> returns a new Quote using the given spec(InstrumentSpecification), stamp(TimeStamp) and values(double...) to set its askPrice(double) and bidPrice(double)<br/>
	 * <strong>3.</strong> TradeIndicationSeries -> returns a new TradeIndication using the given spec(InstrumentSpecification), stamp(TimeStamp) and values(double...) to set its price(double)
	 * @param <U>
	 * @param <T>
	 * @param clazz
	 * @param series
	 * @param spec
	 * @param stamp
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected <U extends MarketDataEntity<U>, T extends TimeSeries<U>>
	U newElement(Class<U> clazz, T series, InstrumentSpecification spec, TimeStamp stamp, double... values) {
		if(CandleSeries.class.isAssignableFrom(series.getClass())) {
			Candle candle = new Candle(spec, stamp);
			candle.setDoubles(values);
			return (U) (Object) candle;
		} else if(QuoteSeries.class.isAssignableFrom(series.getClass())) {
			Quote quote = new Quote(spec, stamp);
			quote.setDoubles(values);
			return (U) (Object) quote;
		} else if(TradeIndicationSeries.class.isAssignableFrom(series.getClass())) {
			TradeIndication trade = new TradeIndication(spec, stamp);
			trade.setDoubles(values);
			return (U) (Object) trade;
		}	
		throw new IllegalArgumentException("Unknown series.");
	}
	/**
	 * if the given element(U) is a:<br/>
	 * <strong>1.</strong> Candle -> returns its openPrice(double), highPrice(double), lowPrice(double) and closePrice(double) as a double[] array<br/>
	 * <strong>2.</strong> Quote -> returns its askPrice(double) and bidPrice(double) as a double[] array<br/>
	 * <strong>3.</strong> TradeIndication -> returns its price(double) as a double[] array
	 * @param <U>
	 * @param element
	 * @return
	 */
	protected <U extends MarketDataEntity<U>>
	double[] getDoubles(U element) {
		if(Candle.class.isAssignableFrom(element.getClass())) {
			return ((Candle) (Object) element).getDoubles();
		} else if(Quote.class.isAssignableFrom(element.getClass())) {
			return ((Quote) (Object) element).getDoubles();
		} else if(TradeIndication.class.isAssignableFrom(element.getClass())) {
			return ((TradeIndication) (Object) element).getDoubles();
		}
		throw new IllegalArgumentException("Unknown MarketDataEntity subclass");
	}
	/**
	 * if the given element(U) is a:<br/>
	 * <strong>1.</strong> Candle -> sets its openPrice(double), highPrice(double), lowPrice(double) and closePrice(double) with the given values(double[]) (must have length=4)<br/>
	 * <strong>2.</strong> Quote -> sets its askPrice(double) and bidPrice(double) with the given values(double[]) (must have length=2)<br/>
	 * <strong>3.</strong> TradeIndication -> sets its price(double) with the given values(double[]) (must have length=1)
	 * @param <U>
	 * @param element
	 * @param values
	 */
	protected <U extends MarketDataEntity<U>>
	void setDoubles(U element, double[] values) {
		if(Candle.class.isAssignableFrom(element.getClass())) {
			((Candle) (Object) element).setDoubles(values);
		} else if(Quote.class.isAssignableFrom(element.getClass())) {
			((Quote) (Object) element).setDoubles(values);
		} else if(TradeIndication.class.isAssignableFrom(element.getClass())) {
			((TradeIndication) (Object) element).setDoubles(values);
		}
		throw new IllegalArgumentException("Unknown MarketDataEntity subclass");
	}
}
